"""
Module contains the mail list which selects the mailbox and the current mail
Written By: Daniel Ortiz
"""
import wx, datetime, time
import wx.gizmos as gizmos
from database.mapclass import *
import wx.lib.customtreectrl as TreeCtrl
from wx.lib.mixins import treemixin
from wx.lib.mixins import listctrl
from email.Utils import parsedate
from operator import attrgetter
from wx import ImageFromStream, BitmapFromImage
import cStringIO

def getSeenData():
    return \
'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\
\x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\
\x00\x02"IDAT8\x8d\x95\xd0\xbdk\x13q\x18\x07\xf0\xef\xe5\xee\xd2K.\xb9\xbc\\\
.m.mB\xb5I\xc5\xb4\xa15E\x1b\t\xa4C\xb5\x83\xadQ\x17\xdf\x82\x0eRP(*q\x10:\
\xdbAP\x8a\xabn\x0e\x82\x8b\xa2\xff\x80\xa0\xb89X\xc1\xda\xa5\xc6\xb3\xa8\
\x88\x96\x92\xd4\xab\xf4r\xf7{\x9c\x14,GH\x9f\xf1y\xf9\xf0<\x0f\x8f\xb6\x91\
\x96\xba\x07J\xb7\xed\xed\xa6\xed\xb4\xcc\xba[\x87\xa7\xddxP\r\x1c\xd1\xf7\
\xce\xd4\xd4\xc4\xe1I\x00\xdc.\x81\x84_KM\x9c\xf6J\x11D\x92\x07/{\xbdjvW\x80\
\x1c\x12\x8bA5{\x9e1\x1b\x81p\x7fD\xed\x1b\xaf\x02\x10:\x04\x94hb\xe0\xc4UI\
\xee\x06\xb1\x16\x98\xd3\x82\x9a*\xdf\xf4z\x83\xfd\x1d\x01~\xbf\xffP(\x9e?\
\xce\x1c\x0b\x00@\xe4\xc0\'\'\xc4\x9eL\xa5\xb6s\xc6\r\x08\xea\xfb\xcf\\\xe3\
\x05\x1f\x88\xd8\xbf$\x11\x83\xa2\xe5/\x01H\xb7\x05B\xf1\x03\x93\x8a64\xc5\
\x98\xbd\xa3B\x90\xe4\xb8\x98\x1a\xbex\xa7\x1d D\xf5\xe2ufm\xa00h\xe2X\xf17b\
\xfc+\x1c\x1d\xdbDy\xd4\x01o\x1bP\xa2\xb9\tY\xee\x1dr;\x1dJ|\xe4\xec\xb9\xd9\
E\xbb\xb1\xb1Nk\xdf\xbe\xd3\xfc\xd3\x97tc\xa9IsO^\xd3\xfb\xd5OD\xc4h\xe1\xee\
#\xd2\xf6T\x9e\xb9m \xaa=\xf9\xda\xad\xf9S\xbc\x12\x8e\xe2\xeb\xafm\xacZ\x02\
|"\x0f_&\x8f7\x1f\xbf\xc0\xb6,\xcc\xcd\x9eD\xa10V\n\xaa\xa33\xff\x01Zo\xb9\
\xaah\xb9\x9c\xdbf\x04\xc0\xcbs\xe08\x801\x07\x96#E\x03\xb1l\x15\x00\xf8\xbf\
\xb7\xeb\x99\xcaC9<\xa8\xbf]Z\xc6p&\x04-\xa2`\xa5n\xc0\x94U\xd8\xc6\n\xa6\
\xf7%\xd1\xda2\xb1\xb0\xf8\x1c+\x9f}\x08(I\xddl\xae\xbd\xe3\x00@\xed+]I\xe5.\
\xdc\x13\x04Id\x8c\x81\xec\x06\xe2!\x13\xe9\xa4\x1f\x8d\xf5:B\x11\x1d\x8d\
\xad.|\xa8\xdb\x10\xbab\xe0<\x1c@\xc0\x0f\xe3\xc5\x03N\x92")=W\xbd\xaf\xea\
\xe3S\xc4Z\xae\xcfu\x0b\x8fG\x84\xd90\x0c!\xa0\x8d<&\xc7\xcan\xfe\\\x06\x91\
\xd31\x00p`\xccJ\xff\x01\xd2\x94\xb5c\x95\x99YS\x00\x00\x00\x00IEND\xaeB`\
\x82' 

def getSeenBitmap():
    return BitmapFromImage(getSeenImage())

def getSeenImage():
    stream = cStringIO.StringIO(getSeenData())
    return ImageFromStream(stream)

#----------------------------------------------------------------------
def getUnseenData():
    return \
'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\
\x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\
\x00\x00\x1cIDAT8\x8dcddbf\xa0\x040Q\xa4{\xd4\x80Q\x03F\r\x18D\x06\x00\x00]b\
\x00&\x87\xd5\x92\xeb\x00\x00\x00\x00IEND\xaeB`\x82' 

#----------------------------------------------------------------------
def getUpArrowData():
    return \
'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\
\x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\
\x00\x01\xc2IDAT8\x8d\x9d\xd2\xcbN\xdb@\x14\x06\xe0\xdf\x8e\x87\x80\x1b\xc7v\
H\x9d\x10\xdb8\x17.qLBJ[\xaa6\xb4\x08\x11\x04\x95\x00\x91\xb4@+\xb1i\xd5nXt\
\xc1\nu\xc1\x92\x15\xcf\xc2\xcb\xb5"\xc9\xcc\xd8\xd3E/j\x84\xb1J\xcf\xf2\xcc\
9\x9f\xfe#\r\x90\\r^\xaf\xad\x00j\xf1\xce\x81\xa4mB\x1e\x04zN\xbbvK\xe5\x9d\
\xff\x02*\x9e\x7f\xfc\xee\xa4\xef9\xb3N\x0f@\xee\x9e\x80\xf2l\xa9\xd58m\x04\
\x8b\xa8\xfb\xb5\xfd\xc2\xf4\\\xe7>\x80\x14\xf8\x8f_\xbf\xe8<5\x077C\x04\xcd\
:\xacb\xfe2.E,@@\x1a\xed\x95\xf6\xd7\xda|\x05\x94RH\x12\xf0duy\xc9\xcaU\xd7\
\xff\x05\x98\xa8\xcc\xb7?ov_\x92\xe1`\x08\x00\x08y\x88r\xc5Ea\xc6\xba\x02@\
\x12\x81\xbcf{\x9d\xb5\xd5S\xdb-\x81s\xfe\xa7\x1fE\x02\xcd\xe5z9\x9b\xb5?&\
\x01\xb2j\x1ag\xbd\xfe.\x19\rGc\x0fB\x08X\x85\xbc\xec\xba\xce\x19\x00%\x160\
\x0c\xbby\xf8\xf6\xe0\x93a\xea\x88\xa2\xe8\xd6m\xb2,\xc1o\xccU\x8dl\xe9<\x16\
\xf0\xdc\xd9\x8b\xdd\xbd\x1d%\x0co/\xff.-\xa3*\x8e\xe3\x9e\xe8\xd0\x8d1`r\
\xd2|\xde{\xd3\xdb\xd4\xb2\x1a \xc4\x9d\x00!)\xb8^q!\xcc\xa4\xbf\x8c\x01\xed\
V\xeb|o\x7f[O\xa5\x12?\'\x84\x00T5-9\xb6{\x00\xa83\xbf\xa6\xd3\xdb\xc7\xef\
\x8f\xd64-\x13{\xfb8 @H\n\x96e<\x9a\x9a\x9a8\x92\x01`\xfdU\xf7p\xab\xbb\x91#\
D\x81H\x88\xffw\n\x85H0\x8d\xe9\xbeD\xc8\xc3\x0f\xcd\xc0\xdfXX\xacU9\xe3\x8c\
2\x0e\xc6\x198\xe7`\x8c\x82s\x0eN9\xc2\x88\x83\xd1\x9f}\xca8\x18\x1dI\xdf\
\xbe\xdf\x0c~\x00\xb6w~\x9b\xfb\x84\x0b\x9a\x00\x00\x00\x00IEND\xaeB`\x82' 

def getUpArrowBitmap():
    return BitmapFromImage(getUpArrowImage())

def getUpArrowImage():
    stream = cStringIO.StringIO(getUpArrowData())
    return ImageFromStream(stream)

#----------------------------------------------------------------------
def getDownArrowData():
    return \
"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\
\x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\
\x00\x01\xfcIDAT8\x8d\x95\x92_k\xd3P\x18\xc6\x9f\x9c\x9c$+i\x12\xff\\HGU(\
\xde\xedF\xfd\x00\x03?\x82\x1fA\xfc\n^\x08^x\xeb\x85\x88\x88N\x90M\xbc\x18\
\x8c\xa2\xa8\x03\x156A\xc3\x9c\xb2\xd5?\xd4Q\xba\xb6t4mS\xd7T\x96\xb6i\xba\
\xb4\xc9\xda\xec\xc2\xdai\xd7\xda\xf9\xc0\xe1\xb9x\x9e\xf3{\xcf\x0b\x87Q\xa4\
\xf0\x15BpI\x14\x03\x91\x89\x89\x80\xc7\xf1\x14\x94\xe3A)\x05\xa5\\\xcf\x7f\
\x1d\xae\xe7<G\x11\x10\x03\xd0\xf5m0\x00p\xfc\xd8\xe9\xd9H$tu2<\tEQ \xcb2$I\
\x86$I\x08\x06\x83\x08\xf6\\\x92$(\xb2\x04Y\x96\x11\n\x9d\xc2\x83\xfb\xb3i\
\x02\x00\xbb\x8e\xfd\xb4\xe5\xec\x99\xbe\xef\xe3(\x12\x04\x1e\xc9d\x1ao^-/\
\x13\x00h\xb7\xabKF\xb9\xb2\xda\xed\x8c\x070\x0c\x83\xae\xefc}\xedk!\xf6\xe5\
\xdd<\xf9\x1d8\xaew\xa3R\xa9\x9a\xe3\x00\x94Rh\xb9<\x9e<^X\x03\x10\xeb\x03\
\x9aM#\xa1m\x15U\xcf\xf5\xfe9\xddu]\xa8\xef?\xfdH\xa6\xe2\xb7\x01\x80\xfcY\
\xf0\xba\xce5\xbdhx\x0c\xc3\x0c\x05\x10B`Y6\xa2\x0b\xd1E\xc0\xf9|\x08P\xaf\
\x1b\xf9lF\x9bk\xb5]\x0cct\xf6:x\x16]4\x7fn\xef\xcc\xf4\xa1\x03\x9d\xeen\xbb\
u'\xbd\xb9\xe5\xb1,=\xf4|\xdb\xb6\xa1\xaa+\xf3M\xcfH\x8c\x02\xa0\xd1(\xe55\
\xad4S\xabY \xe4 fY\x16/\x9e\xbf\xde1k\x95\xbb\x7f\xad5dUW\xd7\xb3\x0f\x13\
\x1b\xa9*\xcfs\xfd\xdd\r\xa3\x82\x0f+\x1f\xe7\xeauC\x1b\x07\x00\xe0f\xb4\x9c\
~\xafP(\x81r\x14<\xcf\xe3\xed\x92j6\xec\xe6#\x00\xfe\x11\x00\xf0\xf3\xc5\xd4\
\xcb\xf4fN\x17\x04\x01\x99t\x16\xf1o\x1b\xb7,\xab\x98\x1b,\xb2#\x00\x00\xbae\
\xbf#\x9c<s6<\xfd=\x9e,\xae\xc7V\xaf\x03\x9d\xb1\x1fm@\xe2\xd4\x85\xf3\xd3\
\xe5s\x91\x8b7\xff\xf3\xe2\x81N(\x91\xcb\x1c'N\x8d\xca\xf7\x01-\x82\xd4\x14;\
\x80\x056\x00\x00\x00\x00IEND\xaeB`\x82" 

def getDownArrowBitmap():
    return BitmapFromImage(getDownArrowImage())

def getDownArrowImage():
    stream = cStringIO.StringIO(getDownArrowData())
    return ImageFromStream(stream)


def getUnseenBitmap():
    return BitmapFromImage(getUnseenImage())

def getUnseenImage():
    stream = cStringIO.StringIO(getUnseenData())
    return ImageFromStream(stream)

class FolderTree(TreeCtrl.CustomTreeCtrl):
    def __init__(self, parent):
        self.parent = parent
        TreeCtrl.CustomTreeCtrl.__init__(self, parent, style = TreeCtrl.TR_HIDE_ROOT | TreeCtrl.TR_HAS_BUTTONS |
                                         TreeCtrl.TR_HAS_VARIABLE_ROW_HEIGHT | TreeCtrl.TR_EDIT_LABELS | TreeCtrl.TR_SINGLE )
        self.Variables()
        self.CreateArt()
        self.BindToHandler()
        self.CreateMetaFolders()    
        self.BindEvents()
    
    def Variables(self):
        """Class-wide variables"""
        self.search_title = 'Search'
        self.mail_title = 'Mail'
        self.sent_title = 'Sent'
        self.trash_title = 'Trash'
        self.draft_title = 'Draft'
        self.inbox_title = 'Inbox'
        self.mailbox_title = 'Mailboxes'
        self.no_query = 'Found 0 matches'
        self.synchronize_title = 'Synchronize:'
        self.dragging = False
        self.citem = None
        self.srange = 0
        self.sgauge = wx.Gauge(self, -1, 20, size = (100, -1))
        self.new_folder = False
        self.delete_added = False
        self.delete_parent = None
        
    def IncGaugeValue(self):
        self.sgauge.SetValue(self.sgauge.GetValue() + 1)
        
    def ResetGaugeValue(self):
        self.sgauge.SetValue(0)
        self.srange = 0
        
    def ChangeSRange(self, srange, new):
        if new and not srange:
            self.ResetGaugeValue()
        self.srange += srange
        self.sgauge.SetRange(self.srange)
        self.Expand(self.mailboxes)
        
    def AddList(self, mail_list):
        self.mail_list = mail_list
        
    def BindToHandler(self):
        d = self.GetTopLevelParent().database_handler
        d.BindFolderTree(self)
        self.handler = d
    
    def CreateArt(self):
        """Create the relevant art of the tree and save in Image List"""
        isize = (16, 16)
        self.il  = wx.ImageList(isize[0], isize[1])
        self.folder = self.il.Add(wx.ArtProvider_GetBitmap(wx.ART_FOLDER, wx.ART_OTHER, isize))
        self.ofolder = self.il.Add(wx.ArtProvider_GetBitmap(wx.ART_FILE_OPEN, wx.ART_OTHER, isize))
        self.nfile = self.il.Add(wx.ArtProvider_GetBitmap(wx.ART_NORMAL_FILE, wx.ART_OTHER, isize))
        self.SetImageList(self.il)
    
    def CreateMetaFolders(self):
        """Create the Meta folders that exist in the TreeCtrl"""
        root = self.AddRoot("Root")
        self.inbox = self.AppendItem(root, self.inbox_title)
        self.SetItemImage(self.inbox, self.folder, which = wx.TreeItemIcon_Normal)
        self.SetItemImage(self.inbox, self.ofolder, which = wx.TreeItemIcon_Expanded)
        self.sent = self.AppendItem(root, self.sent_title)
        self.SetItemImage(self.sent, self.folder, which = wx.TreeItemIcon_Normal)
        self.SetItemImage(self.sent, self.ofolder, which = wx.TreeItemIcon_Expanded)
        self.trash = self.AppendItem(root, self.trash_title)
        self.SetItemImage(self.trash, self.folder, which = wx.TreeItemIcon_Normal)
        self.SetItemImage(self.trash, self.ofolder, which = wx.TreeItemIcon_Expanded)
        self.draft = self.AppendItem(root, self.draft_title)
        self.SetItemImage(self.draft, self.folder, which = wx.TreeItemIcon_Normal)
        self.SetItemImage(self.draft, self.ofolder, which = wx.TreeItemIcon_Expanded)
        self.mailboxes = self.AppendItem(root, self.mailbox_title)
        self.SetItemImage(self.mailboxes, self.folder, which = wx.TreeItemIcon_Normal)
        self.SetItemImage(self.mailboxes, self.ofolder, which = wx.TreeItemIcon_Expanded)
        self.sgauge_item = self.AppendItem(self.mailboxes, self.synchronize_title, wnd = self.sgauge)
        self.meta = [self.inbox, self.sent, self.trash, self.draft, self.mailboxes]
        self.mstring_item = {self.sent_title : self.sent,
                         self.inbox_title : self.inbox,
                         self.draft_title: self.draft,
                         self.trash_title: self.trash}
        self.mitem_string = {self.sent : self.sent_title,
                         self.inbox : self.inbox_title,
                         self.draft: self.draft_title,
                         self.trash: self.trash_title}
        
    def BindEvents(self):
        """Bind the class events"""
        self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelection)
        self.Bind(TreeCtrl.EVT_TREE_BEGIN_LABEL_EDIT, self.OnLabelEdit)
        self.Bind(TreeCtrl.EVT_TREE_END_LABEL_EDIT, self.OnEndLabelEdit)
        self.Bind(wx.EVT_IDLE, self.OnIdle)
        self.Bind(wx.EVT_TREE_KEY_DOWN, self.OnKeyDown)
        #self.Bind(wx.EVT_TREE_BEGIN_DRAG, self.OnBeginDrag)
    
    def OnIdle(self, event):
        if self.delete_added:
            self.delete_added = False
            item = self.GetLastChild(self.delete_parent)
            self.Delete(item
                        )
    def OnKeyDown(self, event):
        """Handle key press"""
        def __FolderDepth(x):
            return x.count('/')
        if event.GetKeyCode() == 8 or event.GetKeyCode() == wx.WXK_DELETE:
            item = self.GetSelection()
            if item:
                if item in self.meta or self.IsMailbox(item):
                    pass
                else:
                    item_list = self.GetItemChildren(item, True) + [item]
                    mailbox = self.__GetMailboxData(item)
                    folder_list = [self.__GetFolderPath(i) for i in item_list]
                    folder_list.sort(cmp = lambda x, y: cmp(__FolderDepth(x), __FolderDepth(y)), reverse = True)
                    item_list.sort(cmp = lambda x, y: cmp(__FolderDepth(self.__GetFolderPath(x)),
                                                           __FolderDepth(self.__GetFolderPath(y))), reverse = True)
                    for f in item_list:
                        self.Delete(f)
                    self.handler.RemoveFolderList(folder_list, mailbox)
                    
    def OnLabelEdit(self, event):
        """Check if the item is editable"""
        item = event.GetItem()
        if item in self.meta:
            event.Veto()
            
    def IsMailbox(self, item):
        return self.GetItemParent(item) == self.mailboxes
   
    def OnEndLabelEdit(self, event):
        """Save the change in name to the folder"""
        item = event.GetItem()
        if not event.IsEditCancelled():
            label = event.GetLabel()
            if self.IsMailbox(item):
                old_label = self.GetItemText(item)
                print old_label, label
                self.handler.UpdateMailboxName(old_label, label)
            else:
                mailbox, old_path = self.GetMailbox(item)
                if old_path.find('/') != -1:
                    new_path = old_path[:old_path.rfind('/')] +'/'+ label
                else:
                    new_path = label
                self.handler.UpdateFolderName(mailbox, new_path, old_path, create = self.new_folder)
                self.new_folder = False
        else:
            self.delete_added = True
            self.delete_parent = self.GetItemParent(item)
        event.Skip()
        
    # Inspired From(almost completely) Tree Mixin by Andrea Gavana     
    def OnBeginDrag(self, event):
        # We allow only one item to be dragged at a time, to keep it simple
        self.dragging = True
        self._dragItem = event.GetItem()
        if self.IsValidDragItem(self._dragItem):
            self.StartDragging()
            event.Allow()
        else:
            event.Veto()

    def OnEndDrag(self, event):
        self.StopDragging()
        dropTarget = event.GetItem()
        if not dropTarget:
            dropTarget = self.GetRootItem()
        if self.IsValidDropTarget(dropTarget):
            self.UnselectAll()
            if dropTarget != self.GetRootItem():
                self.SelectItem(dropTarget)
            self.OnDrop(dropTarget, self._dragItem)

    def OnDragging(self, event):
        if not event.Dragging():
            self.StopDragging()
            return
        item, flags = self.HitTest(wx.Point(event.GetX(), event.GetY()))
        if not item:
            item = self.GetRootItem()
        if self.IsValidDropTarget(item):
            self.SetCursorToDragging()
        else:
            self.SetCursorToDroppingImpossible()
        if flags & wx.TREE_HITTEST_ONITEMBUTTON:
            self.Expand(item)
        if self.GetSelections() != [item]:
            self.UnselectAll()
            if item != self.GetRootItem(): 
                self.SelectItem(item)
        event.Skip()
        
    def StartDragging(self):
        self.Bind(wx.EVT_MOTION, self.OnDragging)
        self.Bind(wx.EVT_TREE_END_DRAG, self.OnEndDrag)
        self.SetCursorToDragging()

    def StopDragging(self):
        self.Unbind(wx.EVT_MOTION)
        self.Unbind(wx.EVT_TREE_END_DRAG)
        self.ResetCursor()
        self.UnselectAll()
        self.SelectItem(self._dragItem)

    def SetCursorToDragging(self):
        self.SetCursor(wx.StockCursor(wx.CURSOR_HAND))
        
    def SetCursorToDroppingImpossible(self):
        self.SetCursor(wx.StockCursor(wx.CURSOR_NO_ENTRY))
        
    def ResetCursor(self):
        self.SetCursor(wx.NullCursor)

    def IsValidDropTarget(self, dropTarget):
        if dropTarget: 
            allChildren = self.GetItemChildren(self._dragItem, recursively=True)
            parent = self.GetItemParent(self._dragItem) 
            return dropTarget not in [self._dragItem, parent] + allChildren and dropTarget not in self.meta
        else:
            return True        

    def IsValidDragItem(self, dragItem):
        """Checks if the item is valid, and also whether it is a meta folder"""
        return dragItem and dragItem != self.GetRootItem() and not dragItem in self.meta
    
    def GetItemChildren(self, item=None, recursively=False):
        """ Return the children of item as a list. """
        if not item:
            item = self.GetRootItem()
            if not item:
                return []
        children = []
        child, cookie = self.GetFirstChild(item)
        while child:
            children.append(child)
            if recursively:
                children.extend(self.GetItemChildren(child, True))
            child, cookie = self.GetNextChild(item, cookie)
        return children

    def OnDrop(self, dropTarget, dragItem):
        text = [self.__GetFolderPath(dropTarget) + '/' + self.__GetFolderPath(c) for c in self.GetItemChildren(dragItem, recursively = True)]
        text += [self.__GetFolderPath(dropTarget) + '/' + self.__GetFolderPath(dragItem)]
        dropPath = self.__GetFolderPath(dropTarget)
        mailbox = self.__GetMailboxData(dropTarget)
        self.AddFolder(mailbox, text)
        self.Delete(dragItem)
        self.handler.MoveFolder(text, dropPath, mailbox)
        
    def OnSelection(self, event):
        if not self.dragging:
            item = event.GetItem()
            self.citem = item
            if item in [self.draft, self.inbox, self.sent, self.trash]:
                mail = self.handler.GetMeta(self.mitem_string[item].lower())
                self.mail_list.AppendMail(mail)
            else:
                mailbox, folder = self.GetMailbox(item)
                if mailbox:
                    mail = self.handler.GetMailboxFolderMail(mailbox, folder)
                    self.mail_list.AppendMail(mail)
                    
    def NewFolder(self):
        def __AddCurrentMailbox():
            mailbox = self.handler.GetCurrentMailbox()
            self.AddFolder(mailbox, ' ', edit = True)
        item = self.GetSelection()
        if item in self.meta:
            __AddCurrentMailbox()
        else:
            mailbox, path = self.GetMailbox(item)
            if mailbox:
                self.AddFolder(mailbox, path + '/' + ' ', edit = True)
            else:
                __AddCurrentMailbox()
        self.new_folder = True
            
    def GetMailbox(self, item):
        """Returns the mailbox corresponding to the current path"""
        if item == self.mailboxes:
            return None, None
        elif self.GetItemParent(item) == self.mailboxes:
            return None, None
        else:
            return self.__GetMailboxData(item), self.__GetFolderPath(item)
        
    def __GetMailboxData(self, item):
        """Concatenates a string of the folder paths"""
        data = None
        item = self.GetItemParent(item)
        while item:
            data = self.GetItemPyData(item)
            item = self.GetItemParent(item)
            if item == self.mailboxes:
                break
        return data
    
    def __GetFolderPath(self, item):
        """Concatenates a string of the folder paths"""
        text = self.GetItemText(item)
        item = self.GetItemParent(item)
        while item:
            text = self.GetItemText(item) + '/' + text
            item = self.GetItemParent(item)
            if item == self.mailboxes:
                break
        return '/'.join(text.split('/')[1:])
        
    def FindItem(self, node, name):
        """Search in a node for an alias"""
        item, cookie = self.GetFirstChild(node)
        while item:
            if self.GetItemText(item) == name:
                return item
            item, cookie = self.GetNextChild(node, cookie)
        return item
    
    def AddMailbox(self, m):
        """Add a mailbox to the tree control"""
        child = self.AppendItem(self.mailboxes, m.alias)
        self.SetItemImage(child, self.folder, which = wx.TreeItemIcon_Normal)
        self.SetItemImage(child, self.ofolder, which = wx.TreeItemIcon_Expanded)
        self.SetItemPyData(child, m)    
        
    def AddFolder(self, mailbox, folder, edit = False):
        """Add in the relevant mailbox"""
        tree = self.FindItem(self.mailboxes, mailbox.alias)
        if isinstance(folder, basestring):
            folder = [folder]
        for alias in folder:
            if alias.find('/'):
                children = alias.split('/')
                item = tree
                for c in children:
                    parent = item
                    item = self.FindItem(item, c)
                    if not item:
                        item = self.AppendItem(parent, c)
                        self.SetItemImage(item, self.folder, which = wx.TreeItemIcon_Normal)
                        self.SetItemImage(item, self.ofolder, which = wx.TreeItemIcon_Expanded)
                        if edit:
                            self.ExpandToMailbox(item)
                            self.ScrollTo(item)
                            self.EditLabel(item)
            else:
                child = self.AppendItem(tree, alias)
                self.SetItemImage(child, self.folder, which = wx.TreeItemIcon_Normal)
                self.SetItemImage(child, self.ofolder, which = wx.TreeItemIcon_Expanded)
    
    def ExpandToMailbox(self, item):
        """Expand all folders to the mailbox root"""
        item = self.GetItemParent(item)
        while item:
            if item == self.mailboxes:
                break
            self.Expand(item)
            item = self.GetItemParent(item)
                
    def InTrash(self):
        """Returns true if the currently selected folder is the trash meta folder"""
        return self.citem == self.trash

class ListOfMail(object):
    def __init__(self):
        pass

class MailList(wx.ListCtrl):
    def __init__(self, parent):
        self.parent = parent
        wx.ListCtrl.__init__(self, parent, style = wx.LC_REPORT | wx.LC_VIRTUAL)
        self.BindEvents()
        self.BindToHandler()
        self.CreateListItems()
        self.Variables()
        self.CreateColumns()

        
    def CreateListItems(self):
        """Create image based list items"""
        self.isize = (16, 16)
        self.il = wx.ImageList(self.isize[0], self.isize[1])
        self.seen = self.il.Add(getSeenBitmap())
        self.unseen = self.il.Add(getUnseenBitmap())
        self.uparrow = self.il.Add(getUpArrowBitmap())
        self.downarrow = self.il.Add(getDownArrowBitmap())
        self.empty = self.il.Add(wx.EmptyBitmap(self.isize[0], self.isize[1]))
        self.SetImageList(self.il, wx.IMAGE_LIST_SMALL)
    
    def BindToHandler(self):
        d = self.GetTopLevelParent().database_handler
        d.BindMailList(self)
        self.handler = d
        
    def BindTree(self, folder_tree):
        self.folder_tree = folder_tree
    
    def BindEvents(self):
        """Bind the relevant MailList events"""
        self.Bind(wx.EVT_SIZE, self.OnSize)
        self.Bind(wx.EVT_IDLE, self.OnIdle)
        self.parent.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected, self)
        self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
        self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick)

    def Variables(self):
        """Class wide variables"""
        self.resize = False
        self.search = False
        self.columns = [self.seen, "From", "Subject", "Date", ""] 
        self.mail_list = []
        self.item_text = ["", "from_who", "subject", "orig_date", ""]
        self.image_map = {'': self.unseen, 'from_who' : self.unseen, 'subject' : self.unseen, 'orig_date' : self.unseen}
        self.HighlightCol = -1
        
    def OnItemSelected(self, event):
        self.cindex = event.m_itemIndex
        if self.mail_list:
            mail = self.mail_list[self.cindex]
            mailbox = self.handler.GetMailboxMail(mail)
            self.handler.ShowMail(mail, mailbox)
            
    def OnKeyDown(self, event):
        """Control keystrokes that occur in the list ctrl"""
        if event.GetKeyCode() == 8 or event.GetKeyCode() == wx.WXK_DELETE:
            if event.ShiftDown():
                self.RemoveItemsFromTrash()
            else:
                self.DeleteSelection()
                
    def OnColClick(self, event):
        """Process a column click. Resort the data"""
        if self.HighlightCol == event.GetColumn():
            self.reverse = not self.reverse
        else:
            self.reverse = True
        self.HighlightCol = event.GetColumn()
        self.SortItems()
        
    def SortItems(self, keep_direction = False):
        """Sort the relevant items in the list"""
        def toDate(x):
            return datetime.datetime.fromtimestamp(time.mktime(parsedate(x)))
        text = self.item_text[self.HighlightCol]
        if text == 'orig_date':
            self.mail_list.sort(cmp = lambda x, y: cmp(toDate(x), toDate(y)), key = attrgetter(text), reverse = self.reverse)
        elif not text: 
            if not self.HighlightCol:
                self.mail_list.sort(cmp = lambda x, y: cmp(x.find('\\Seen'), y.find('\\Seen')), key = attrgetter('flags'), reverse = self.reverse)
        else:
            self.mail_list.sort(key = attrgetter(text), reverse = self.reverse)
        self.UpdateColumnImages(text, self.reverse, keep_direction = keep_direction)
    
    def UpdateColumnImages(self, select_text, reverse, keep_direction = False):
        """Change all of the relevant column images"""
        for text, image in self.image_map.iteritems():
            col_item = self.GetColumnByName(text)
            if select_text == text:
                image = self.GetColumn(col_item).GetImage()
                if not keep_direction:
                    if image == self.uparrow:
                        image = self.downarrow
                    else:
                        image = self.uparrow
            else:
                image = self.unseen
            print image == self.downarrow
            if col_item:
                self.SetColumnImage(col_item, image)
                
    def GetColumnByName(self, name):
        """Returns the column that corresponds to name"""
        for ncol, col in enumerate(self.item_text):
            if col == name:
                return ncol
        
    # Virtual callbacks
    def OnGetItemText(self, item, col):
        mail = self.mail_list[item]
        attr = self.item_text[col]
        if attr:
            obj = getattr(mail, attr)
            return obj or ''
        else:
            return attr
        
    def OnGetItemImage(self, item):
        mail = self.mail_list[item]
        if not "\\Seen" in mail.flags:
            return self.seen
        else:
            return self.unseen

    def RemoveItemsFromTrash(self):
        """Remove items from the trash"""
        items = self.GetSelections()
        for item in items: 
            if self.folder_tree.InTrash():
                mail = self.mail_list[item]
                mailbox = self.handler.GetMailboxMail(mail)
                self.handler.UnMarkAsDeleted(mail, mailbox)
                self.DeleteItem(item)
                    
    def DeleteSelection(self):
        items = self.GetSelections()
        for item in items:  
            mail = self.mail_list[item]
            mailbox = self.handler.GetMailboxMail(mail)
            self.DeleteItem(item)
            if self.folder_tree.InTrash():
                self.handler.Delete(mail, mailbox)
            else:
                self.handler.MarkAsDeleted(mail, mailbox)
                        
    def GetSelections(self):
        """Return a list of selected items"""
        ret = []
        item = self.GetFirstSelected()
        while item != -1:
            ret.append(item)
            item = self.GetNextSelected(item)
        return ret
    
    def CreateColumns(self):
        """Create the relevant columns in the ListCtrl"""
        for key, c in enumerate(self.columns):
            if isinstance(c, basestring):
                self.InsertColumn(key, c)
            else:
                self.InsertColumn(key, "")
                self.SetColumnImage(key, c)
        
    def OnSize(self, event):
        self.resize = True
        event.Skip()
        
    def AppendMail(self, mail):
        if not self.search:
            self.DeleteAllItems()
            self.mail_list = mail
            self.SetItemCount(len(self.mail_list))
            if self.HighlightCol >= 0:
                self.SortItems(keep_direction = True)
        if mail:
            self.SetColumnWidth(self.GetColumnCount() - 1, 0)
        else:
            self.SetColumnWidth(self.GetColumnCount() - 1, self.isize[0]) 
            
    def AddSearchResults(self, mail):
        self.plist = self.mail_list[:]
        self.AppendMail(mail)
        self.search = True
        wx.media.MEDIACTRLPLAYERCONTROLS_DEFAULT
    def EndSearchResults(self):
        self.search = False
        self.AppendMail(self.plist)
        
    def UpdateColumnSize(self, offset = 0):
        size = self.GetSize()
        esize = size[0] - self.isize[0] * 3
        print self.isize[0]
        for col, text in enumerate(self.columns):
            if isinstance(text, basestring):
                if text:
                    self.SetColumnWidth(col, (esize) / (len(self.columns) - 2))
                else:
                    self.SetColumnWidth(col, self.isize[0])
            else:
                if text == self.seen:
                    self.SetColumnWidth(col, self.isize[0] * 2)
        
    def OnIdle(self, event):
        if self.resize:
            self.UpdateColumnSize()
            self.Refresh()
            self.resize = False

class MailTreeList(gizmos.TreeListCtrl):
    def __init__(self, parent):
        self.parent = parent
        gizmos.TreeListCtrl.__init__(self, parent, style = wx.TR_DEFAULT_STYLE |  wx.TR_EDIT_LABELS | wx.TR_HIDE_ROOT |
                                     wx.TR_FULL_ROW_HIGHLIGHT)
        self.ClassVariables()
        self.InstantiateArt()
        self.CreateColumns()
        self.BindEvents()
        self.BindToHandler()
        self.CreateMetaFolders()
        
    def ClassVariables(self):
        """Instantiate all of the class wide variables"""
        self.resize = False
        self.columns = ['From', 'Subject', 'Date']
        self.search_title = 'Search'
        self.mail_title = 'Mail'
        self.sent_title = 'Sent'
        self.trash_title = 'Trash'
        self.draft_title = 'Draft'
        self.inbox_title = 'Inbox'
        self.mailbox_title = 'Mailboxes'
        self.no_query = 'Found 0 matches'
        
    def CreateMetaFolders(self):
        """Create the meta folders for the visualization"""
        root = self.AddRoot("Hidden")
        self.inbox = self.AppendItem(root, self.inbox_title)
        self.SetItemImage(self.inbox, self.folder, which = wx.TreeItemIcon_Normal)
        self.SetItemImage(self.inbox, self.ofolder, which = wx.TreeItemIcon_Expanded)
        self.sent = self.AppendItem(root, self.sent_title)
        self.SetItemImage(self.sent, self.folder, which = wx.TreeItemIcon_Normal)
        self.SetItemImage(self.sent, self.ofolder, which = wx.TreeItemIcon_Expanded)
        self.trash = self.AppendItem(root, self.trash_title)
        self.SetItemImage(self.trash, self.folder, which = wx.TreeItemIcon_Normal)
        self.SetItemImage(self.trash, self.ofolder, which = wx.TreeItemIcon_Expanded)
        self.draft = self.AppendItem(root, self.draft_title)
        self.SetItemImage(self.draft, self.folder, which = wx.TreeItemIcon_Normal)
        self.SetItemImage(self.draft, self.ofolder, which = wx.TreeItemIcon_Expanded)
        self.mailboxes = self.AppendItem(root, self.mailbox_title)
        self.SetItemImage(self.mailboxes, self.folder, which = wx.TreeItemIcon_Normal)
        self.SetItemImage(self.mailboxes, self.ofolder, which = wx.TreeItemIcon_Expanded)
        self.meta = [self.inbox, self.sent, self.trash, self.draft, self.mailboxes]
        self.meta_map = {self.sent_title : self.sent,
                         self.inbox_title : self.inbox,
                         self.draft_title: self.draft,
                         self.trash_title: self.trash}
        
    def InstantiateArt(self):
        """Instantiates all of the bitmaps used for the class"""
        isize = (16, 16)
        self.il  = wx.ImageList(isize[0], isize[1])
        self.folder = self.il.Add(wx.ArtProvider_GetBitmap(wx.ART_FOLDER, wx.ART_OTHER, isize))
        self.ofolder = self.il.Add(wx.ArtProvider_GetBitmap(wx.ART_FILE_OPEN, wx.ART_OTHER, isize))
        self.nfile = self.il.Add(wx.ArtProvider_GetBitmap(wx.ART_NORMAL_FILE, wx.ART_OTHER, isize))
        self.SetImageList(self.il)
        
    def CreateColumns(self):
        """Function draws and creates the columns of the Tree List"""
        for col, text in enumerate(self.columns):
            self.AddColumn(text)
            
    def BindEvents(self):
        """Function binds the MailList to all relevant wx events"""
        self.Bind(wx.EVT_SIZE, self.OnSize)
        self.Bind(wx.EVT_IDLE, self.OnIdle)
        self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnSelection)
        self.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus)
        self.Bind(wx.EVT_SET_FOCUS, self.OnSetFocus)
        self.Bind(wx.EVT_TREE_KEY_DOWN, self.OnKeyDown)
        self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown)
        
    def OnSize(self, event):
        """Resize the window"""
        self.resize = True
        event.Skip()
        
    def OnIdle(self, event):
        """Will resize the window when the window gets an idle event"""
        if self.resize:
            size = self.parent.GetSize()
            for col, text in enumerate(self.columns):
                self.SetColumnWidth(col, (size[0] - len(self.columns)) / len(self.columns))
            self.Refresh()
            self.resize = False
    
    def OnRightDown(self, event):
        """When an object is right-clicked a menu will popup"""
        pass
            
    def BindToHandler(self):
        """Binds the window to a handler which stores the data"""
        d = self.GetTopLevelParent().database_handler
        d.BindVisualization(self)
        self.handler = d
    
    def NewSearch(self, str):
        """New search box with the current menu"""
        root = self.GetRootItem()
        if not self.FindItem(root, self.search_title):
            child = self.PrependItem(root, self.search_title)
            self.SetItemImage(child, self.folder, which = wx.TreeItemIcon_Normal)
            self.SetItemImage(child, self.ofolder, which = wx.TreeItemIcon_Expanded)
        item = self.FindItem(root, self.search_title)
        self.DeleteChildren(item)
        child = self.AppendItem(item, str)
        self.SetItemImage(child, self.folder, which = wx.TreeItemIcon_Normal)
        self.SetItemImage(child, self.ofolder, which = wx.TreeItemIcon_Expanded)
        self.Expand(item)
        
    def FindItem(self, node, name):
        """Search in a node for an alias"""
        item, cookie = self.GetFirstChild(node)
        while item:
            if self.GetItemText(item) == name:
                return item
            item, cookie = self.GetNextChild(node, cookie)
        return item
    
    def FindItemByData(self, node, data):
        """Search a node for a piece of mail"""
        item, cookie = self.GetFirstChild(node)
        while item:
            if repr(self.GetItemPyData(item)) == repr(data):
                return item
            item, cookie = self.GetNextChild(node, cookie)
        return item
    
    def PrintItemData(self, node, data):
        """Print the item data in a node"""
        item, cookie = self.GetFirstChild(node)
        while item:
            print 'Data', self.GetItemPyData(item), data, self.GetItemText(item)
            item, cookie = self.GetNextChild(node, cookie)
        return item
    
    
    def AppendSearchResults(self, str, results):
        """Append the results of the search to the window"""
        item = self.GetItemFromPath([self.search_title, str])  
        if results:
            self.AppendMail(item, results)
        else:  
            child = self.AppendItem(item, self.no_query)
            self.SetItemImage(child, self.nfile, which = wx.TreeItemIcon_Normal)

    def GetItemFromPath(self, path):
        """From the path get the item"""
        item = self.GetRootItem()
        for p in path:
            item = self.FindItem(item, p)
        return item
    
    def AddMailboxList(self, mailbox):
        """Add a mailbox to the the treectrl. The mailbox is an instance of the
        mapclass.Mailbox"""
        for m in mailbox:
            self.AddMailbox(m)
    
    def AddMailbox(self, m):
        child = self.AppendItem(self.mailboxes, m.alias)
        self.SetItemImage(child, self.folder, which = wx.TreeItemIcon_Normal)
        self.SetItemImage(child, self.ofolder, which = wx.TreeItemIcon_Expanded)
        self.SetItemPyData(child, m)    

    def AddMail(self, mailbox, folder_mail):
        """Add the relevant folders to a mailbox"""
        tree = self.FindItem(self.mailboxes, mailbox.alias)
        folders = folder_mail.keys()
        for f in folders:
            if 'META' in f:
                self.AppendMail(self.meta_map[f.split(':')[1]], folder_mail[f], mailbox)
            else:
                alias = f
                if alias.find('/'):
                    children = alias.split('/')
                    item = tree
                    for c in children:
                        parent = item
                        item = self.FindItem(item, c)
                        if not item.IsOk():
                            item = self.AppendItem(parent, c)
                            self.SetItemImage(item, self.folder, which = wx.TreeItemIcon_Normal)
                            self.SetItemImage(item, self.ofolder, which = wx.TreeItemIcon_Expanded)
                        if children[-1] == c:
                            self.AppendMail(item, folder_mail[f], mailbox)
                else:
                    child = self.AppendItem(tree, alias)
                    self.SetItemImage(child, self.folder, which = wx.TreeItemIcon_Normal)
                    self.SetItemImage(child, self.ofolder, which = wx.TreeItemIcon_Expanded)
                
    def AppendMail(self, item, mail, mailbox):
        """Appends mail to an item"""
        for m in mail:
            child = self.AppendItem(item, self.mail_title)
            self.SetItemText(child, str(m.from_who), 0)
            self.SetItemText(child, str(m.subject), 1)
            self.SetItemText(child, str(m.orig_date), 2)
            self.SetItemImage(child, self.nfile, which = wx.TreeItemIcon_Normal)
            self.SetItemPyData(child, [m, mailbox])
            
    def ExpandMailbox(self, m):
        """Expand the relevant mailbox"""
        item = self.FindItem(self.mailboxes, m.alias)
        self.ExpandAllChildren(item)
        
    def HighlightMailbox(self, m):
        """Select the current mailbox"""
        item = self.FindItem(self.mailboxes, m.alias)
        self.SelectItem(item, True)
        
    def OnSelection(self, event):
        """When an item is selected display it!"""
        item = event.GetItem()
        if self.GetItemImage(item) == self.nfile:
            mail, mailbox = self.GetItemPyData(item)
            self.handler.ShowMail(mail, mailbox)
                
    def OnKillFocus(self, event):
        """Save the current focus state"""
        self.focus = False
        print "inside"
        
    def OnSetFocus(self, event):
        """Save the current focus state"""
        self.focus = True
        print "here"
        
    def IsFocused(self):
        """Return the focus state"""
        return self.focus

    def AddSend(self, mail, temporary = False, success = False):
        """Add a piece of mail to the send folder"""
        if temporary:
            child = self.PrependItem(self.sent, self.mail_title)
            self.SetItemText(child, "Sending...", 0)
            self.SetItemText(child, str(mail.subject), 1)
            self.SetItemText(child, str(mail.orig_date), 2)
            self.SetItemImage(child, self.nfile, which = wx.TreeItemIcon_Normal)
            self.SetItemPyData(child, Mail(*mail.get()))
        else:
            if success:
                self.PrintItemData(self.sent, mail)
                item = self.FindItemByData(self.sent, mail)
                self.SetItemText(item, "Sent", 0)
            else:
                wx.MessageBox("Failed to send message %s to %s" % (mail.subject, mail.to))
    
    def AddSearchResults(self, mail, mailbox):
        """Append search results"""
        root = self.GetRootItem()
        for m in mail:
            child = self.PrependItem(root, self.mail_title)
            self.SetItemText(child, str(m.from_who), 0)
            self.SetItemText(child, str(m.subject), 1)
            self.SetItemText(child, str(m.orig_date), 2)
            self.SetItemImage(child, self.nfile, which = wx.TreeItemIcon_Normal)
            self.SetItemPyData(child, [m, mailbox])
     
    def CullToMeta(self):
        """Remove all found mail except for the meta folders"""
        root = self.GetRootItem()
        item, cookie = self.GetFirstChild(root)
        delete_list = []
        while item:
            if not item in self.meta:
                delete_list.append(item)
            item, cookie = self.GetNextChild(root, cookie)
        for item in delete_list:
            self.Delete(item)
    
    def OnKeyDown(self, event):
        """Multiplex the key down event"""
        if event.GetKeyCode() == 8 or event.GetKeyCode() == wx.WXK_DELETE:
            e = event.GetKeyEvent()
            if e.ShiftDown():
                self.RemoveItemsFromTrash()
            else:
                self.DeleteSelection()
    
    def RemoveItemsFromTrash(self):
        """Remove items from the trash"""
        items = self.GetSelections()
        for item in items:
            if self.GetItemImage(item) == self.nfile:    
                if self.InTrash(item):
                    mail, mailbox = self.GetItemPyData(item)
                    self.handler.UnMarkAsDeleted(mail, mailbox)
                    self.Delete(item)
                    self.AddMail(mailbox, {mail.folder: [mail]})
                    f = self.handler.GetMetaFolder(mail)
                    if f:
                        self.AddMail(mailbox, {f : [mail]})
    
    def DeleteSelection(self):
        """Deleting the current selection"""
        items = self.GetSelections()
        for item in items:
            if self.GetItemImage(item) == self.nfile:    
                mail, mailbox  = self.GetItemPyData(item)
                self.Delete(item)
                if self.InTrash(item):
                    self.handler.Delete(mail, mailbox)
                else:
                    self.handler.MarkAsDeleted(mail, mailbox)
                    self.AppendMail(self.trash, [mail], mailbox)
            
    def InTrash(self, item):
        """Function checks if an item is in the trash meta folder"""
        while item:
            if item == self.trash:
                return True
            item = self.GetItemParent(item)
        return False